/**
 * 
 */
package com.populardata;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;


/**
 * 
 * @author Bill Shirley, Frazer, Ltd.
 */
@Stateless
public class AddressLookupBean
	implements AddressLookupRemote {

	@PersistenceContext(unitName = "AddressLookupPU")
	protected EntityManager manager;

	public AddressLookup lookupZip(int zipcode) {
		Query q = manager.createNamedQuery("LookupZipCode");
		q.setParameter("zipcode", zipcode);
		AddressLookup zip = (AddressLookup)q.getSingleResult();
		return zip;
	}
	
	@SuppressWarnings("unchecked")
	public Set<AddressLookup> getAddressLookupsForCity(String stateName,
			String cityName) {
		Query q = manager.createNamedQuery("getAddressLookupsForCity");
		q.setParameter("state", stateName);
		q.setParameter("city", cityName);
		return new HashSet(q.getResultList());
	}

	@SuppressWarnings("unchecked")
	public Set<AddressLookup> getAddressLookupsForState(String stateName) {
		Query q = manager.createNamedQuery("getAddressLookupsForState");
		q.setParameter("state", stateName);
		return new HashSet<AddressLookup>(q.getResultList());
	}

	@SuppressWarnings("unchecked")
	public Set<AddressLookup> getAddressLookupsNear(float latitude,
			float longitude, int miles) {
		float lat1, lat2, long1, long2;
		lat1 = latitude - miles/102;
		lat2 = latitude + miles/102;
		long1 = longitude - miles/102;
		long2 = longitude + miles/102;

		Query q = manager.createNamedQuery("getAddressLookupsNear");
		q.setParameter("lat1", lat1);
		q.setParameter("lat2", lat2);
		q.setParameter("long1", long1);
		q.setParameter("long2", long2);
		Set<AddressLookup> result = new HashSet<AddressLookup>(q.getResultList());
		return result;
	}

	@SuppressWarnings("unchecked")
	public Set<String> getCityNamesForState(String stateName) {
		Query q = manager.createNamedQuery("getCityNamesForState");
		q.setParameter("state", stateName);
		return new HashSet<String>(q.getResultList());
	}

	@SuppressWarnings("unchecked")
	public Set<String> getAllStateNames(boolean onlyStates) {
		// This retrieves from the database instead of hardcoding, so that
		// customization can be done by pruning the database (say, of unwanted
		// territories or unused states).
		
		Query q = manager.createNamedQuery("getAllStateNames");
		List<String> list = q.getResultList();
		Set<String> results = new HashSet<String>(list.size());
		
		if (onlyStates) {
			for(String state : list)
				if (statesOnly.containsValue(state))
					results.add(state);
		} else {
			results.addAll(list);
		}
		
		return results;
	}


	public String stateNameFromCode(String code) {
		if (states.containsKey(code))
			return states.get(code);
		else
			return code;
	}
	
	//
	// Prime the database
	//
	private boolean isLoaded() {
		Query q = manager.createNamedQuery("getAddressLookupCount");
		Long count = (Long)q.getSingleResult();
		if (count > 10) // arbitrary value
			return true;
		
		return false;
	}

	
	private static Map<String, String> statesOnly;
	private static Map<String, String> states;
	private void fillStateMap() {
		// http://www.usps.com/ncsc/lookups/usps_abbreviations.html
		states = new HashMap<String, String>();
		statesOnly = new HashMap<String, String>();
		statesOnly.put("AL", "Alabama");
		statesOnly.put("AK", "Alaska");
		statesOnly.put("AZ", "Arizona");
		statesOnly.put("AR", "Arkansas");
		statesOnly.put("CA", "California");
		statesOnly.put("CO", "Colorado");
		statesOnly.put("CT", "Connecticut");
		statesOnly.put("DE", "Delaware");
		statesOnly.put("FL", "Florida");
		statesOnly.put("GA", "Georgia");
		statesOnly.put("HI", "Hawaii");
		statesOnly.put("ID", "Idaho");
		statesOnly.put("IL", "Illinois");
		statesOnly.put("IN", "Indiana");
		statesOnly.put("IA", "Iowa");
		statesOnly.put("KS", "Kansas");
		statesOnly.put("KY", "Kentucky");
		statesOnly.put("LA", "Louisiana");
		statesOnly.put("ME", "Maine");
		statesOnly.put("MD", "Maryland");
		statesOnly.put("MA", "Massachusetts");
		statesOnly.put("MI", "Michigan");
		statesOnly.put("MN", "Minnesota");
		statesOnly.put("MS", "Mississippi");
		statesOnly.put("MO", "Missouri");
		statesOnly.put("MT", "Montana");
		statesOnly.put("NE", "Nebraska");
		statesOnly.put("NV", "Nevada");
		statesOnly.put("NH", "New Hampshire");
		statesOnly.put("NJ", "New Jersey");
		statesOnly.put("NM", "New Mexico");
		statesOnly.put("NY", "New York");
		statesOnly.put("NC", "North Carolina");
		statesOnly.put("ND", "North Dakota");
		statesOnly.put("OH", "Ohio");
		statesOnly.put("OK", "Oklahoma");
		statesOnly.put("OR", "Oregon");
		statesOnly.put("PA", "Pennsylvania");
		statesOnly.put("RI", "Rhode Island");
		statesOnly.put("SC", "South Carolina");
		statesOnly.put("SD", "South Dakota");
		statesOnly.put("TN", "Tennessee");
		statesOnly.put("TX", "Texas");
		statesOnly.put("UT", "Utah");
		statesOnly.put("VT", "Vermont");
		statesOnly.put("VA", "Virginia");
		statesOnly.put("WA", "Washington");
		statesOnly.put("WV", "West Virginia");
		statesOnly.put("WI", "Wisconsin");
		statesOnly.put("WY", "Wyoming");
		statesOnly.put("DC", "District of Columbia");
		
		states.putAll(statesOnly);
		states.put("MP", "Northern Mariana Islands");
		states.put("MH", "Marshall Islands");
		states.put("AS", "American Samoa");
		
		states.put("AP", "Armed Forces Pacific");
		states.put("AA", "Armed Forces Americas"); // - Canada
		states.put("AE", "Armed Forces Europe"); // + Canada + Africa + Asia + Middle East
		states.put("PR", "Puerto Rico");
		states.put("VI", "The Virgin Islands");
		states.put("GU", "Guam");
		states.put("PW", "Palao");
		states.put("FM", "Federated States of Micronesia");
	}
	
	private static final String ZIPCODEFILENAME = "ZIP_CODES.csv";
	
	/**
	 * Checks the data to see if it's loaded into the database.  If not, it takes the time
	 * to do so.  This is invoked by the persistence container after it is instantiated.
	 */
	// @PostConstruct - caused createNamedQuery to throw null pointer exception
	public int primeTheData() {
		
		if (states == null)
			fillStateMap();
		
		if (isLoaded())
			return 0;

		
		InputStream stream = this.getClass().getResourceAsStream(ZIPCODEFILENAME);
		
		//URL url = new URL("http://www.populardata.com/zip_codes.zip");
		//InputStream stream = url.openStream();
		
		if (stream == null) {
			System.err.println(ZIPCODEFILENAME + " could not be found as a resource.");
			return -1;
		}
		InputStreamReader reader = new InputStreamReader(stream);
		BufferedReader in = new BufferedReader(reader);
		String line;
		int linecount = 0;
		int citylength = 0;
		int countylength = 0;
		
		try {
			line = in.readLine();
		} catch (IOException e1) {
			line = null;
			e1.printStackTrace();
		}
		while (line != null) {
			String fields[] = line.split(",");
			
			try {
				for (int i=0; i<fields.length; i++)
					fields[i] = fields[i].replace("\"", "");

				int i1 = 0;
				if (fields[0].length() > 0) i1 = Integer.parseInt(fields[0]);
				float f1 = 0;
				if (fields[1].length() > 0) f1 = Float.parseFloat(fields[1]);
				float f2 = 0;
				if (fields[2].length() > 0) f2 =Float.parseFloat(fields[2]);
				
				if (fields.length == 7) {
					AddressLookup z = new AddressLookup(
						i1,
						f1,
						f2,
						AddressLookup.initialCaps(fields[3]),
						stateNameFromCode(fields[4]),
						AddressLookup.initialCaps(fields[5]),
						fields[6]
					);
				
					manager.persist(z);
					
					if (citylength < fields[3].length())
						citylength = fields[3].length();
					if (countylength < fields[5].length())
						countylength = fields[5].length();
					
				}
			} catch (Exception e) {
				System.err.println("===========");
				for (int i = 0; i<fields.length; i++)
					System.err.println("fields["+i+"] = " + fields[i]);
				e.printStackTrace(System.err);
				return -2;
			}
			
			linecount++;
			try {
				line = in.readLine();
			} catch (IOException e) {
				line = null;
				e.printStackTrace();
			}
		}
	
		System.out.println("[[[ Succeeded in priming zip code data ]]] " + linecount 
				+ " -  " + citylength + "/" + countylength);
		
		return linecount;
	}



	
}
